/**
 * 玩家行动日志工厂（Redis 存储）
 *
 * 目标：统一记录玩家在何时做了什么，获得/失去/应得/应失的物品，用于后期运维、补发与扣除。
 *
 * 使用示例：
 * import ActionLog from '../model/ActionLogFactory.js'
 * await ActionLog.log({
 *   uid: 'U123',
 *   action: '副本掉落结算',
 *   source: '副本',
 *   layer: 10,
 *   gained: [ { 类别:'圣遗物', 套装名称:'风灵遗章', 名称:'风灵之花', 部位:'花', 星级:4, 稀有度:'稀有', id:60111, 数量:1 } ],
 *   lost: [ { 类别:'材料', 名称:'灵木碎片', id:40001, 数量:2 } ],
 *   shouldGain: [ { 类别:'丹药', 名称:'十品气血丹', id:30010, 数量:1 } ],
 *   shouldLose: [ { 类别:'材料', 名称:'符纹纸', id:40003, 数量:1 } ],
 *   success: true,
 *   reason: ''
 * })
 *
 * 查询示例：
 * const recent = await ActionLog.getUserLogs('U123', 50)
 * const global = await ActionLog.getGlobalLogs(100)
 *
 * 审计记录示例：
 * await ActionLog.audit({ uid:'U123', shouldGain:[...], shouldLose:[...], reason:'掉落计算异常' })
 */
import lodash from 'lodash'
import crypto from 'crypto'
import Data from '../components/Data.js'

function now(){
  const ts = Date.now()
  const iso = new Date(ts).toISOString()
  const day = iso.slice(0,10).replace(/-/g,'')
  return { ts, iso, day }
}

function genTraceId(){
  try {
    return crypto.randomBytes(8).toString('hex')
  } catch {
    return Math.random().toString(16).slice(2)
  }
}

function normalizeItems(arr){
  const a = Array.isArray(arr) ? arr : []
  return a.map(it=>({
    类别: it['类别'] || '',
    套装名称: it['套装名称'],
    元素: it['元素'],
    名称: it['名称'] || '',
    部位: it['部位'],
    星级: it['星级'],
    稀有度: it['稀有度'],
    id: it['id'],
    数量: lodash.isNumber(it['数量']) && it['数量']>0 ? it['数量'] : 1
  }))
}

function keys(uid){
  const { day } = now()
  return {
    user: `xiuxian:log:player:${uid}`,
    global: `xiuxian:log:global`,
    day: `xiuxian:log:day:${day}`
  }
}

const MAX_LOG = 10000

async function readLogArray(key){
  try {
    const txt = await redis.get(key)
    if (!txt) return []
    const arr = JSON.parse(txt)
    return Array.isArray(arr) ? arr : []
  } catch (e){
    console.log(e)
    return []
  }
}

async function writeLogArray(key, arr){
  try {
    await redis.set(key, JSON.stringify(arr))
    return true
  } catch (e){
    console.log(e)
    return false
  }
}

async function pushToRedis(listKey, entry){
  const arr = await readLogArray(listKey)
  arr.unshift(entry)
  if (arr.length > MAX_LOG) arr.length = MAX_LOG
  return await writeLogArray(listKey, arr)
}

async function log({ uid, action, source, layer, gained, lost, shouldGain, shouldLose, success=true, reason='', context={} }){
  const t = now()
  const entry = {
    trace_id: genTraceId(),
    ts: t.ts,
    iso: t.iso,
    uid: uid || 'unknown',
    action: action || '未知动作',
    source: source || '',
    layer: lodash.isNumber(layer) ? layer : undefined,
    success: !!success,
    reason: reason || '',
    gained: normalizeItems(gained),
    lost: normalizeItems(lost),
    should_gain: normalizeItems(shouldGain),
    should_lose: normalizeItems(shouldLose),
    context: context || {}
  }
  const k = keys(entry.uid)
  await pushToRedis(k.user, entry)
  await pushToRedis(k.global, entry)
  await pushToRedis(k.day, entry)
  return entry
}

async function audit({ uid, shouldGain, shouldLose, reason }){
  return await log({ uid, action:'审计记录', source:'系统', layer: undefined, gained:[], lost:[], shouldGain, shouldLose, success:false, reason })
}

async function getUserLogs(uid, limit=50, offset=0){
  const k = keys(uid)
  const arr = await readLogArray(k.user)
  const start = Math.max(0, offset)
  const end = Math.min(arr.length, start + limit)
  return arr.slice(start, end)
}

async function getGlobalLogs(limit=100, offset=0){
  const listKey = 'xiuxian:log:global'
  const arr = await readLogArray(listKey)
  const start = Math.max(0, offset)
  const end = Math.min(arr.length, start + limit)
  return arr.slice(start, end)
}

export default { log, audit, getUserLogs, getGlobalLogs }
